#region Copyright Notice
// 
// 
// Copyright (c) 2009-2010 Anindya Chatterjee
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// 
// 
#endregion
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using GCodeIssueTracker.Comments;
using GCodeIssueTracker.Query;
using GCore;

namespace GCodeIssueTracker
{
    /// <summary>
    /// Google Code - project hosting service class.
    /// </summary>
    /// <remarks>This class cannot be extended.</remarks>
    public sealed partial class ProjectHostingService : GService<IssuesFeed, IssuesEntry>
    {
        private ProjectHostingIssueCommentService _commentService;

        /// <summary>
        /// Instantiate a new <see cref="ProjectHostingService"/> object.
        /// </summary>
        /// <param name="projectName">The project name.</param>
        public ProjectHostingService(string projectName)
        {
            ProjectName = projectName;
        }

        /// <summary>
        /// Instantiate a new <see cref="ProjectHostingService"/> object.
        /// </summary>
        /// <param name="projectName">The project name</param>
        /// <param name="userName">The Google account username.</param>
        /// <param name="password">The Google account password.</param>
        public ProjectHostingService(string projectName, string userName, string password)
            : base(userName, password)
        {
            ProjectName = projectName;
        }

        /// <summary>
        /// Instantiate a new <see cref="ProjectHostingService"/> object.
        /// </summary>
        /// <param name="projectName">The project name</param>
        /// <param name="userName">The Google account username.</param>
        /// <param name="password">The Google account password.</param>
        /// <param name="proxy">Web proxy to be used, if behind a firewall.</param>
        public ProjectHostingService(string projectName, string userName, string password, WebProxy proxy)
            : base(userName, password, proxy)
        {
            ProjectName = projectName;
        }

        /// <summary>
        /// Gets and sets the Google Code Project name
        /// </summary>
        public string ProjectName { get; private set; }

        /// <summary>
        /// Gets issues for a project as a <see cref="IssuesFeed"/> object. Max result is 25.
        /// </summary>
        /// <returns>An <see cref="IssuesFeed"/> object.</returns>
        /// <remarks>To get all issues use the GetAllIssues method.</remarks>
        public IssuesFeed GetIssues()
        {
            GUrl = "http://code.google.com/feeds/issues/p/" + ProjectName + "/issues/full";
            return GetAllFeed();
        }

        /// <summary>
        /// Gets all the issues for a project as a <see cref="IssuesFeed"/> object.
        /// </summary>
        /// <returns>An <see cref="IssuesFeed"/> object.</returns>
        public IssuesFeed GetAllIssues()
        {
            var query = new IssueQuery();
            query.AppendQuery(QueryParamType.MaxResults, Int32.MaxValue.ToString());
            return QueryIssue(query);
        }

        /// <summary>
        /// Get the summery of all the issues for a project as a <see cref="IssuesFeed"/>.
        /// </summary>
        /// <returns>An <see cref="IssuesFeed"/> object.</returns>
        /// <remarks>The resultant <see cref="IssuesFeed"/> doesnot contain all the data, but a summery of data.<para>
        /// This method gets a quick summery of all the issues by means of a csv file download.
        /// </para></remarks>
        public IssuesFeed GetAllIssuesSummery()
        {
            string url = "http://code.google.com/p/" + ProjectName +
                         "/issues/csv?can=1&q=&colspec=ID%20Type%20Status%20Milestone%20Priority%20Stars%20Owner%20Summary";
            var client = new WebClient();
            if (ProxySettings != null)
                client.Proxy = ProxySettings;
            client.Credentials = new NetworkCredential
                                     {
                                         UserName = GUserName,
                                         Password = GPassword
                                     };

            string tempFile = Path.GetTempFileName();
            client.DownloadFile(url, tempFile);
            IssuesFeed issueFeed = GetIssueFromCSV(tempFile);
            File.Delete(tempFile);
            return issueFeed;
        }

        /// <summary>
        /// Submits a new <see cref="IssuesEntry"/> into the project.
        /// </summary>
        /// <param name="newEntry">A new issue entry.</param>
        /// <param name="applicationName">Application name invoking the submission.</param>
        /// <returns>A new <see cref="IssuesEntry"/> object containing some additional data.</returns>
        public IssuesEntry SubmitNewIssue(IssuesEntry newEntry, string applicationName)
        {
            if (string.IsNullOrEmpty(applicationName))
                applicationName = "GCodeIssueTracker-1.0";

            GUrl = "http://code.google.com/feeds/issues/p/" + ProjectName + "/issues/full";
            return SubmitNewEntry(newEntry, applicationName);
        }

        /// <summary>
        /// Queries the project service to get issues depending on certain query parameters.
        /// </summary>
        /// <param name="query">The data about the query paramteres.</param>
        /// <returns>Issues satisfying the query.</returns>
        public IssuesFeed QueryIssue(IGQuery query)
        {
            GUrl = "http://code.google.com/feeds/issues/p/" + ProjectName + "/issues/full";
            return Query(query);
        }

        /// <summary>
        /// Gets all comments for a particular issue.
        /// </summary>
        /// <param name="issueId">Issue id</param>
        /// <returns>All comments of a particular issue.</returns>
        public IssueCommentsFeed GetAllIssueComments(string issueId)
        {
            _commentService = new ProjectHostingIssueCommentService(this);
            return _commentService.GetAllIssueComments(issueId);
        }

        /// <summary>
        /// Submit new comments for a particular issue.
        /// </summary>
        /// <param name="newEntry">New comment entry.</param>
        /// <param name="issueId">Id for the issue.</param>
        /// <param name="applicationName">Application which is invoking this call.</param>
        /// <returns>A new <see cref="IssueCommentsEntry"/> object containing some additional data.</returns>
        public IssueCommentsEntry SubmitNewIssueComment(IssueCommentsEntry newEntry, string issueId,
                                                        string applicationName)
        {
            _commentService = new ProjectHostingIssueCommentService(this);
            return _commentService.SubmitNewIssueComment(newEntry, issueId, applicationName);
        }

        private static IssuesFeed GetIssueFromCSV(string csvFile)
        {
            var fileStream = new FileStream(csvFile, FileMode.Open);
            var streamReader = new StreamReader(fileStream);

            string csv = streamReader.ReadToEnd();

            streamReader.Close();
            fileStream.Close();

            return ParseCSV(csv);
        }

        private static IssuesFeed ParseCSV(string csv)
        {
            var reader = new StringReader(csv);
            var issuesFeed = new IssuesFeed
                                 {
                                     Entries = new List<IssuesEntry>()
                                 };

            reader.ReadLine(); // ignore the csv header

            string text;
            while (!string.IsNullOrEmpty(text = reader.ReadLine()))
            {
                issuesFeed.Entries.Add(ParseEntry(text));
            }
            return issuesFeed;
        }

        private static IssuesEntry ParseEntry(string line)
        {
            line = line.Replace("\",\"", "~`*");
            line = line.Replace("\"", "");
            string[] elements = line.Split(new[] {"~`*"}, StringSplitOptions.None);
            if (elements.Length == 9)
            {
                var entry = new IssuesEntry
                                {
                                    Id = int.Parse(elements[0]),
                                    Status = elements[2],
                                    Stars = elements[5],
                                    Owner = new Owner {UserName = elements[6]},
                                    Title = elements[7],
                                    Labels = new List<string>()
                                };

                entry.Labels.AddRange(elements[8].Split(','));
                return entry;
            }
            throw new ArgumentException("Can not create IssueFeed from CSV.",
                                        new Exception("Can not parse the line \"" + line + "\" to create an IssueEntry."));
        }

        #region Nested type: ProjectHostingIssueCommentService

        internal partial class ProjectHostingIssueCommentService : GService<IssueCommentsFeed, IssueCommentsEntry>
        {
            private readonly ProjectHostingService _parentService;

            internal ProjectHostingIssueCommentService(ProjectHostingService parentService)
            {
                _parentService = parentService;
                GPassword = parentService.GPassword;
                GUserName = parentService.GUserName;
                ProxySettings = parentService.ProxySettings;
            }

            internal IssueCommentsFeed GetAllIssueComments(string issueId)
            {
                GUrl = "http://code.google.com/feeds/issues/p/" + _parentService.ProjectName + "/issues/" + issueId +
                       "/comments/full";
                return GetAllFeed();
            }

            internal IssueCommentsEntry SubmitNewIssueComment(IssueCommentsEntry newEntry, string issueId,
                                                              string applicationName)
            {
                GUrl = "http://code.google.com/feeds/issues/p/" + _parentService.ProjectName + "/issues/" + issueId +
                       "/comments/full";
                return SubmitNewEntry(newEntry, applicationName);
            }
        }

        #endregion
    }
}